/* *********************************************************
 * spi屏驱动库
 * 只实现
 * 初始化void LCD_Init(void)；
 * 画点void LCD_DrawPoint(uint16_t x,uint16_t y,uint16_t Color);
 * 两个基本功能函数。
 * 若要实现GUI建议使用开源GUI库LVGL
 * **********************************************************
 * STM32CUBEMX设置
 * SPI  LL库
 * Mode:Full-Duplex Master
 * Hardware NSS Signal:Disable
 * Frame Format:Motorola
 * Data Size:8 Bits
 * First Bit:MSB First
 * Prescaler:分频使Baud Rate小于30MBits/s
 * Baud Rate:/
 * Clock Polarity:Low
 * Clock Phase:1 Edge
 * 剩下默认
 * 注意事项：
 * 1.在生成的初始化函数USER CODE中添加LL_SPI_Enable(SPIX)函数
 * ***********************************************************
 * Copyright (c) xsusa 2023.1.9
 */
#include "spi_lcd.h"
#include "stm32h7xx_ll_dma.h"
#define WRITE_DATA_BEGIN LCD_CS_SET(0);LCD_RS_SET(1);
#define WRITE_DATA_END LCD_CS_SET(1);
#define SPI_TX_ENABLE LL_SPI_StartMasterTransfer(LCD_SPI)
static uint8_t SPI_WriteByte(uint8_t Data);

static void LCD_WriteCmd(uint8_t Data);

static void LCD_WriteData(uint8_t Data);

static void LCD_WriteReg(uint8_t LcdReg, uint16_t LcdRegVal);

static void LCD_MemoryWrite(void);

static void LCD_WriteData16Bit(uint16_t Data);

static void LCD_RESET(void);

//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;

/*
 * @函数：SPI写一字节。
 *       STM32CUBEMX设置
 *       Mode:Full-Duplex Master
 *       Frame Format:Motorola
 *       Data Size:8Bits
 *       First Bit:MSB First
 *       Prescaler:自定义
 *       Clock Polarity:Low
 *       Clock Phase:1 Edge
 *       LL库
 * @参数：Data 写入字节
 * @返回：0
 */
static uint8_t SPI_WriteByte(uint8_t Data) {
    LL_SPI_StartMasterTransfer(LCD_SPI);
    while (!LL_SPI_IsActiveFlag_TXP(LCD_SPI));
    LL_SPI_TransmitData8(LCD_SPI, Data);

    //LCD_SPI->CR1|=SPI_CR1_CSTART;
    //while((LCD_SPI->SR&SPI_SR_TXP)!=SPI_SR_TXP);
    //*((__IO uint8_t *)&LCD_SPI->TXDR) = Data;
    return 0;
}

//#define SPI_WriteByte(n) LCD_SPI->CR1|=SPI_CR1_CSTART;\
while((LCD_SPI->SR&SPI_SR_TXP)!=SPI_SR_TXP);\
*((__IO uint8_t *)&LCD_SPI->TXDR) = Data
/*
 * @函数：写指令
 * @参数：Data
 */
static void LCD_WriteCmd(uint8_t Data) {
    LCD_CS_SET(0);
    LCD_RS_SET(0);
    SPI_WriteByte(Data);
    LCD_CS_SET(1);
}

/*
 * @函数：写数据
 * @参数：Data
 */
static void LCD_WriteData(uint8_t Data) {
    LCD_CS_SET(0);
    LCD_RS_SET(1);
    SPI_WriteByte(Data);
    LCD_CS_SET(1);
}

/*
 * @函数：写寄存器
 * @参数：LcdReg LCD寄存器
 * @参数：LcdRegVal 寄存器值
 */
static void LCD_WriteReg(uint8_t LcdReg, uint16_t LcdRegVal) {
    LCD_WriteCmd(LcdReg);
    LCD_WriteData(LcdRegVal);
}

/*
 * @函数：将图像数据从主机处理器传输到帧存储器，执行此函数前需先写入行地址和页地址
 */
static void LCD_MemoryWrite(void) {
    LCD_WriteCmd(lcddev.wramcmd);
}

/*
 * @函数：写16位数据;为提高速度，CS,RS的写数据设置不放置在此函数内，在LCD_WriteData16Bit前后调用WRITE_DATA_BEGIN和WRITE_DATA_END，循环写16位数据时则在循环前后调用WRITE_DATA_BEGIN和WRITE_DATA_END
 * @参数：Data 16位RGB565格式
 */
static void LCD_WriteData16Bit(uint16_t Data) {
#ifndef ILI9486
    SPI_WriteByte(Data>>8);
    SPI_WriteByte(Data);
#endif
#ifdef ILI9486
    //ILI9486只能以RGB666格式写像素，每字节高6bit+低位2任意bit，按R G B顺序发送
    //需将RGB565转为RGB666格式写像素
    //RGB666格式：R5 R4 R3 R2 R1 R0 X X G5 G4 G3 G2 G1 G0 X X B5 B4 B3 B2 B1 B0 X X
    // X为任意bit
    //RGB565格式：R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0
    SPI_WriteByte(Data >> 8);//R4 R3 R2 R1 R0 G5 G4 G3
    SPI_WriteByte((Data >> 3));//G5 G4 G3 G2 G1 G0 B4 B3
    SPI_WriteByte((Data << 3));//B4 B3 B2 B1 B0 0 0 0
#endif
}

/*
 * @函数：LCD复位
 */
static void LCD_RESET(void) {
    LCD_RST_SET(0);
    for (int i = 0; i < 100 * 10000; i++);
    LCD_RST_SET(1);
    for (int i = 0; i < 50 * 10000; i++);
}

/*
 * @函数：清屏
 * @参数：Color 清屏颜色
 */
void LCD_Clear(uint16_t Color) {
    unsigned int i, m;
    LCD_SetWindows(0, 0, lcddev.width - 1, lcddev.height - 1);
    WRITE_DATA_BEGIN
    for (i = 0; i < lcddev.height; i++) {
        for (m = 0; m < lcddev.width; m++) {
            LCD_WriteData16Bit(Color);
        }
    }
    WRITE_DATA_END
}

/*
 * @函数：LCD初始化
 */
void LCD_Init(void) {
    LCD_RESET(); //LCD 复位
#ifdef ST7735R
    LCD_WriteCmd(0x11);//Sleep exit
    LCD_DELAY(120);
   //ST7735R Frame Rate
   LCD_WriteCmd(0xB1);
   LCD_WriteData(0x01);
   LCD_WriteData(0x2C);
   LCD_WriteData(0x2D);
   LCD_WriteCmd(0xB2);
   LCD_WriteData(0x01);
   LCD_WriteData(0x2C);
   LCD_WriteData(0x2D);
   LCD_WriteCmd(0xB3);
    LCD_WriteData(0x01);
    LCD_WriteData(0x2C);
    LCD_WriteData(0x2D);
    LCD_WriteData(0x01);
   LCD_WriteData(0x2C);
   LCD_WriteData(0x2D);
    LCD_WriteCmd(0xB4); //Column inversion
   LCD_WriteData(0x07);
   //ST7735R Power Sequence
   LCD_WriteCmd(0xC0);
   LCD_WriteData(0xA2);
   LCD_WriteData(0x02);
   LCD_WriteData(0x84);
   LCD_WriteCmd(0xC1);
   LCD_WriteData(0xC5);
   LCD_WriteCmd(0xC2);
   LCD_WriteData(0x0A);
   LCD_WriteData(0x00);
   LCD_WriteCmd(0xC3);
   LCD_WriteData(0x8A);
   LCD_WriteData(0x2A);
   LCD_WriteCmd(0xC4);
    LCD_WriteData(0x8A);
    LCD_WriteData(0xEE);
    LCD_WriteCmd(0xC5); //VCOM
   LCD_WriteData(0x0E);
   LCD_WriteCmd(0x36); //MX, MY, RGB mode
   LCD_WriteData(0xC8);
   //ST7735R Gamma Sequence
   LCD_WriteCmd(0xe0);
    LCD_WriteData(0x0f);
   LCD_WriteData(0x1a);
    LCD_WriteData(0x0f);
    LCD_WriteData(0x18);
    LCD_WriteData(0x2f);
    LCD_WriteData(0x28);
   LCD_WriteData(0x20);
    LCD_WriteData(0x22);
    LCD_WriteData(0x1f);
    LCD_WriteData(0x1b);
   LCD_WriteData(0x23);
   LCD_WriteData(0x37);
   LCD_WriteData(0x00);
    LCD_WriteData(0x07);
   LCD_WriteData(0x02);
    LCD_WriteData(0x10);
   LCD_WriteCmd(0xe1);
   LCD_WriteData(0x0f);
    LCD_WriteData(0x1b);
    LCD_WriteData(0x0f);
    LCD_WriteData(0x17);
    LCD_WriteData(0x33);
    LCD_WriteData(0x2c);
    LCD_WriteData(0x29);
    LCD_WriteData(0x2e);
    LCD_WriteData(0x30);
    LCD_WriteData(0x30);
    LCD_WriteData(0x39);
    LCD_WriteData(0x3f);
    LCD_WriteData(0x00);
    LCD_WriteData(0x07);
    LCD_WriteData(0x03);
    LCD_WriteData(0x10);
    LCD_WriteCmd(0x2a);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x7f);
    LCD_WriteCmd(0x2b);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x9f);
    LCD_WriteCmd(0xF0); //Enable test command
    LCD_WriteData(0x01);
    LCD_WriteCmd(0xF6); //Disable ram power save mode
    LCD_WriteData(0x00);
    LCD_WriteCmd(0x3A); //65k mode
    LCD_WriteData(0x05);
    LCD_WriteCmd(0x29);//Display on
#endif
#ifdef ST7796S
    //ST7796S初始化
    LCD_WriteCmd(0xF0);
    LCD_WriteData(0xC3);
    LCD_WriteCmd(0xF0);
    LCD_WriteData(0x96);
    LCD_WriteCmd(0x36);
    LCD_WriteData(0x68);
   LCD_WriteCmd(0x3A);
   LCD_WriteData(0x05);
    LCD_WriteCmd(0xB0);
    LCD_WriteData(0x80);
    LCD_WriteCmd(0xB6);
    LCD_WriteData(0x00);
    LCD_WriteData(0x02);
    LCD_WriteCmd(0xB5);
    LCD_WriteData(0x02);
    LCD_WriteData(0x03);
    LCD_WriteData(0x00);
    LCD_WriteData(0x04);
    LCD_WriteCmd(0xB1);
    LCD_WriteData(0x80);
    LCD_WriteData(0x10);
    LCD_WriteCmd(0xB4);
    LCD_WriteData(0x00);
    LCD_WriteCmd(0xB7);
    LCD_WriteData(0xC6);
    LCD_WriteCmd(0xC5);
    LCD_WriteData(0x24);
    LCD_WriteCmd(0xE4);
    LCD_WriteData(0x31);
    LCD_WriteCmd(0xE8);
    LCD_WriteData(0x40);
    LCD_WriteData(0x8A);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x29);
    LCD_WriteData(0x19);
    LCD_WriteData(0xA5);
    LCD_WriteData(0x33);
    LCD_WriteCmd(0xC2);
    LCD_WriteCmd(0xA7);

    LCD_WriteCmd(0xE0);
    LCD_WriteData(0xF0);
    LCD_WriteData(0x09);
    LCD_WriteData(0x13);
    LCD_WriteData(0x12);
    LCD_WriteData(0x12);
    LCD_WriteData(0x2B);
    LCD_WriteData(0x3C);
    LCD_WriteData(0x44);
    LCD_WriteData(0x4B);
    LCD_WriteData(0x1B);
    LCD_WriteData(0x18);
    LCD_WriteData(0x17);
    LCD_WriteData(0x1D);
    LCD_WriteData(0x21);

    LCD_WriteCmd(0XE1);
    LCD_WriteData(0xF0);
    LCD_WriteData(0x09);
    LCD_WriteData(0x13);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x0D);
    LCD_WriteData(0x27);
    LCD_WriteData(0x3B);
    LCD_WriteData(0x44);
    LCD_WriteData(0x4D);
    LCD_WriteData(0x0B);
    LCD_WriteData(0x17);
    LCD_WriteData(0x17);
    LCD_WriteData(0x1D);
    LCD_WriteData(0x21);

    LCD_WriteCmd(0X36);
    LCD_WriteData(0xEC);
    LCD_WriteCmd(0xF0);
    LCD_WriteData(0xC3);
    LCD_WriteCmd(0xF0);
    LCD_WriteData(0x69);
    LCD_WriteCmd(0X13);
    LCD_WriteCmd(0X11);
    LCD_WriteCmd(0X29);
#endif
#ifdef ST7789V
    LCD_WriteCmd(0x11);//Sleep exit
    LCD_DELAY (120);
    LCD_WriteCmd(0x36);
    LCD_WriteData(0x00);
    LCD_WriteCmd(0x3a);
    LCD_WriteData(0x05);
    //ST7789v Frame Rate
    LCD_WriteCmd(0xb2);
    LCD_WriteData(0x0c);
    LCD_WriteData(0x0c);
    LCD_WriteData(0x00);
    LCD_WriteData(0x33);
    LCD_WriteData(0x33);
    LCD_WriteCmd(0xb7);
    LCD_WriteData(0x35);

    LCD_WriteCmd(0xbb);
    LCD_WriteData(0x1c);
    LCD_WriteCmd(0xc0);
    LCD_WriteData(0x2c);
    LCD_WriteCmd(0xc2);
    LCD_WriteData(0x01);
    LCD_WriteCmd(0xc3);
    LCD_WriteData(0x0b);
    LCD_WriteCmd(0xc4);
    LCD_WriteData(0x20);
    LCD_WriteCmd(0xc6);
    LCD_WriteData(0x0f);
    LCD_WriteCmd(0xd0);
    LCD_WriteData(0xa4);
    LCD_WriteData(0xa1);

    //ST7735R Gamma Sequence
    LCD_WriteCmd(0xe0);
    LCD_WriteData(0xd0);
    LCD_WriteData(0x00);
    LCD_WriteData(0x03);
    LCD_WriteData(0x09);
    LCD_WriteData(0x13);
    LCD_WriteData(0x1c);
    LCD_WriteData(0x3a);
    LCD_WriteData(0x55);
    LCD_WriteData(0x48);
    LCD_WriteData(0x18);
    LCD_WriteData(0x12);
    LCD_WriteData(0x0e);
    LCD_WriteData(0x19);
    LCD_WriteData(0x1e);
    LCD_WriteCmd(0xe1);
    LCD_WriteData(0xd0);
    LCD_WriteData(0x00);
    LCD_WriteData(0x03);
    LCD_WriteData(0x09);
    LCD_WriteData(0x05);
    LCD_WriteData(0x25);
    LCD_WriteData(0x3a);
    LCD_WriteData(0x55);
    LCD_WriteData(0x25);
    LCD_WriteData(0x3a);
     LCD_WriteData(0x55);
     LCD_WriteData(0x50);
    LCD_WriteData(0x3d);
    LCD_WriteData(0x1c);
    LCD_WriteData(0x1d);
    LCD_WriteData(0x1d);
    LCD_WriteData(0x1e);
    LCD_WriteCmd(0x29);//Display on
#endif
#ifdef ILI9486
    LCD_WriteCmd(0x11);//Sleep exit
    LCD_DELAY(120);

    LCD_WriteCmd(0XF2);
    LCD_WriteData(0x18);
    LCD_WriteData(0xA3);
    LCD_WriteData(0x12);
    LCD_WriteData(0x02);
    LCD_WriteData(0XB2);
    LCD_WriteData(0x12);
    LCD_WriteData(0xFF);
    LCD_WriteData(0x10);
    LCD_WriteData(0x00);
    LCD_WriteCmd(0XF8);
    LCD_WriteData(0x21);
    LCD_WriteData(0x04);

    LCD_WriteCmd(0x13);

    LCD_WriteCmd(0x36);
    LCD_WriteData(0x78);

    LCD_WriteCmd(0xB4);
    LCD_WriteData(0x02);

    LCD_WriteCmd(0xB6);
    LCD_WriteData(0x02);
    LCD_WriteData(0x22);

    LCD_WriteCmd(0xC1);
    LCD_WriteData(0x41);

    LCD_WriteCmd(0xc5);
    LCD_WriteData(0x00);
    LCD_WriteData(0x18);

    LCD_WriteCmd(0x3a);
    LCD_WriteData(0x66);
    LCD_DELAY(50);
    LCD_WriteCmd(0xB0);
    LCD_WriteData(0x80);

    LCD_WriteCmd(0xE0);
    LCD_WriteData(0x0F);
    LCD_WriteData(0x1F);
    LCD_WriteData(0x1C);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x0F);
    LCD_WriteData(0x08);
    LCD_WriteData(0x48);
    LCD_WriteData(0x98);
    LCD_WriteData(0x37);
    LCD_WriteData(0x0A);
    LCD_WriteData(0x13);
    LCD_WriteData(0x04);
    LCD_WriteData(0x11);
    LCD_WriteData(0x0D);
    LCD_WriteData(0x00);
    LCD_WriteCmd(0xE1);
    LCD_WriteData(0x0F);
    LCD_WriteData(0x32);
    LCD_WriteData(0x2E);
    LCD_WriteData(0x0B);
    LCD_WriteData(0x0D);
    LCD_WriteData(0x05);
    LCD_WriteData(0x47);
    LCD_WriteData(0x75);
    LCD_WriteData(0x37);
    LCD_WriteData(0x06);
    LCD_WriteData(0x10);
    LCD_WriteData(0x03);
    LCD_WriteData(0x24);
    LCD_WriteData(0x20);
    LCD_WriteData(0x00);

    LCD_WriteCmd(0x11);
    LCD_DELAY(120);
    LCD_WriteCmd(0x29);
    LCD_WriteCmd(0x2C);
#endif
    LCD_Direction(USE_HORIZONTAL);//设置LCD显示方向
    //LCD_LED=1;//点亮背光
    LCD_Clear(0xFFFF);//清全屏白色
}

/*
 * @函数：设置绘制窗口
 * @参数：XS X轴起始点
 * @参数：XE X轴终点
 * @参数：YS Y轴起始点
 * @参数：YE Y轴终点
 */
void LCD_SetWindows(uint16_t XS, uint16_t YS, uint16_t XE, uint16_t YE) {
    LCD_WriteCmd(lcddev.setxcmd);
    LCD_WriteData(XS >> 8);
    LCD_WriteData(0x00FF & XS);
    LCD_WriteData(XE >> 8);
    LCD_WriteData(0x00FF & XE);

    LCD_WriteCmd(lcddev.setycmd);
    LCD_WriteData(YS >> 8);
    LCD_WriteData(0x00FF & YS);
    LCD_WriteData(YE >> 8);
    LCD_WriteData(0x00FF & YE);

    LCD_MemoryWrite();    //开始写入GRAM
}

/*
 * @函数：设置光标位置/设置绘制点位置
 * @参数：Xpos X轴位置
 * @参数：Ypos Y轴位置
 */
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos) {
    LCD_SetWindows(Xpos, Ypos, Xpos, Ypos);
}

/*
 * @函数：设置LCD显示方向
 * @参数：Direction
 */
void LCD_Direction(uint8_t Direction) {
    lcddev.setxcmd = 0x2A;
    lcddev.setycmd = 0x2B;
    lcddev.wramcmd = 0x2C;
    switch (Direction) {
        case 0:
            lcddev.width = LCD_W;
            lcddev.height = LCD_H;
            LCD_WriteReg(0x36, (1 << 3) | (0 << 6));
            break;
        case 1:
            lcddev.width = LCD_H;
            lcddev.height = LCD_W;
            LCD_WriteReg(0x36,  (1 << 3) | (1 << 5));
            break;
        case 2:
            lcddev.width = LCD_W;
            lcddev.height = LCD_H;
            LCD_WriteReg(0x36, (1 << 3) | (1 << 7));
            break;
        case 3:
            lcddev.width = LCD_H;
            lcddev.height = LCD_W;
            LCD_WriteReg(0x36, (1 << 3) | (1 << 7) | (1 << 6) | (1 << 5));
            break;
        default:
            break;
    }
}

/*
 * @函数：画点
 * @参数：x
 * @参数：y
 */
void LCD_DrawPoint(uint16_t X, uint16_t Y, uint16_t Color) {
    LCD_SetCursor(X, Y);//设置光标位置
    WRITE_DATA_BEGIN
    LCD_WriteData16Bit(Color);
    WRITE_DATA_END
}

/*
 * @函数：绘制区域LVGL专用；注释掉disp_flush()函数内的绘点示例，替换为此函数
 * @参数：XS即area->x1
 * @参数：YS即area->y1
 * @参数：XE即area->x2
 * @参数：YE即area->y1
 * @参数：Color即(uint16_t*)color_p
 */
void LVGL_LCD_Fill(uint16_t XS, uint16_t YS, uint16_t XE, uint16_t YE, uint16_t *Color) {
    uint16_t height, width;
    uint16_t i, j;
    width = XE - XS;
    height = YE - YS;
    LCD_SetWindows(XS, YS, width, height);
    WRITE_DATA_BEGIN
    for (i = 0; i <= height; i++) {
        for (j = 0; j <= width; j++) {
            LCD_WriteData16Bit(*Color);
            Color++;
        }
    }

}
/*
void LVGL_LCD_DMA_Fill(uint16_t XS, uint16_t YS, uint16_t XE, uint16_t YE, uint16_t *Color){
    uint16_t height, width;
    width = XE - XS;
    height = YE - YS;
    LCD_SetWindows(XS, YS, width, height);
    WRITE_DATA_BEGIN
    LL_DMA_ConfigAddresses(DMA1,LL_DMA_STREAM_1,*Color,(uint32_t)LCD_SPI->TXDR,LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
    LL_DMA_SetDataLength(DMA1,LL_DMA_STREAM_1,(uint32_t)(width*height));
    LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);
    LL_SPI_EnableDMAReq_TX(LCD_SPI);
}
*/